/* eslint-disable @typescript-eslint/no-var-requires */
import { promises as fs, existsSync } from "fs";
import path from "path";
import { fileURLToPath } from "url";

import * as core from "./core.mjs";

const IGNORE_COMPONENT_TYPES = [
	"root",
	"page",
	"tab",
	"tabs",
	"column",
	"columns",
	"step",
	"steps",
	"html",
	"repeater",
];

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const srcFile = (...args) => path.resolve(__dirname, "..", "src", ...args);

const storyFile = (...args) => srcFile("stories", "core_components", ...args);

const relPath = (from, to) => path.relative(path.dirname(from), to);

async function loadComponents() {
	const rawComponents = await core.loadComponents();
	// eslint-disable-next-line no-console
	return rawComponents.map((component) => {
		return {
			nameTrim: component.name.replaceAll(/\s/g, ""),
			...component,
		};
	});
}

function generateImports(component, { filePath, srcPath }) {
	return `/* eslint-disable prettier/prettier */
/* 
 * --------------------------------------------------------------
 * This is an autogenerated file. Do not edit this file directly! 
 * --------------------------------------------------------------
 */

import type { Meta, StoryObj } from "@storybook/vue3";
import { provide, ref, computed } from "vue";
import ${component.nameTrim} from "${relPath(filePath, component.fileRef)}";
import injectionKeys from "${srcPath("injectionKeys")}";
import "remixicon/fonts/remixicon.css";
import { generateCore } from "${srcPath("stories", "fakeCore")}";`;
}

function generateArgDefault(field) {
	if (field.default !== undefined) {
		return `			table: {
				defaultValue: {
					summary: ${JSON.stringify(field.default)},
				},
			},
`;
	}
	return "";
}

function generateDescription(field) {
	if (field.desc) {
		return `			description: "${field.desc.replaceAll('"', '\\"')}",`;
	}
	return "";
}

function generateStyles(component) {
	return Object.entries(component.fields)
		.map(([key, field]) => {
			if (field.category === "Style") {
				return `						"--${key}": args.${key},`;
			}
			return null;
		})
		.filter((x) => x !== null)
		.join("\n");
}

function generateStylesInfill(component) {
	const styles = generateStyles(component);
	return `					...(Object.fromEntries(Object.entries({
${styles}
					}).filter(([, value]) => value !== undefined))),`;
}
function mappedControl(field) {
	switch (field.type) {
		case "Number":
			return `			control: "number",
			type: 'number',`;
		case "Color":
			return `			control: "color",
			type: 'string',`;
		case "Key-Value":
			return `			control: "object",`;
		default:
			if (field.options) {
				return `			control: "select",
			options: ${JSON.stringify(Object.keys(field.options))},
			type: 'string',`;
			}
			return `			control: "text",
			type: 'string',`;
	}
}
function generateArgTypes(component) {
	return Object.entries(component.fields)
		.map(([key, field]) => {
			return `		${key}: {
${mappedControl(field)}
${generateDescription(field)}
${generateArgDefault(field)}		},`;
		})
		.join("\n");
}

function generateArgWrap(component) {
	return Object.entries(component.fields)
		.map(([key, field]) => {
			if (field.type === "Object" || field.type === "Key-Value") {
				return `				${key}: computed(() => JSON.parse(args.${key})),`;
			}
			if (field.type === "Number") {
				return `				${key}: computed(() => args.${key} ? Number.parseInt(args.${key}, 10) : args.${key}),`;
			}
			return `				${key}: computed(() => args.${key}),`;
		})
		.join("\n");
}

function generateInitArgValues(component) {
	return Object.entries(component.fields)
		.map(([key, field]) => {
			if (field.init !== undefined || field.default !== undefined) {
				return `		${key}: ${JSON.stringify(field.init || field.default)},`;
			}
			return null;
		})
		.filter((x) => x !== null)
		.join("\n");
}

function generateMeta(component, { module }) {
	return `

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
const meta = {
	title: "core-components/${module}/${component.nameTrim}",
	component: ${component.nameTrim},
	// This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/writing-docs/autodocs
	tags: ["autodocs"],
	argTypes: {
${generateArgTypes(component)}
	},
	args: {
${generateInitArgValues(component)}
	},
} satisfies Meta<typeof ${component.nameTrim}>;

export default meta;
type Story = StoryObj<typeof meta>;`;
}

function generateStory(component) {
	return `

export const Sample: Story = {
	render: (args) => ({
		components: { ${component.nameTrim} },
		setup() {
			const wf = generateCore();
			const rootStyle = computed(() => {
				return {
					"--accentColor": "#5551ff",
					"--buttonColor": "#ffffff",
					"--emptinessColor": "#e9eef1",
					"--separatorColor": "rgba(0, 0, 0, 0.07)",
					"--primaryTextColor": "#202829",
					"--buttonTextColor": "#ffffff",
					"--secondaryTextColor": "#5d7275",
					"--containerBackgroundColor": "#ffffff",
${generateStylesInfill(component)}
					"width": "100%",
					"outline": "none",
					"--notificationsDisplacement": "0",
					"font-family": "Poppins",
					"font-size": "0.8rem",
					"color": "var(--primaryTextColor)",
					"background": "white",
					"min-height": "100%",
					"isolation": "isolate",
				};
			});
			provide(injectionKeys.evaluatedFields, {
${generateArgWrap(component)}
			});
			provide(injectionKeys.instanceData, [ref({})]);
			provide(injectionKeys.isDisabled, ref(false));
			provide(injectionKeys.componentId, "test");
			provide(injectionKeys.isBeingEdited, ref(false));
			provide(injectionKeys.instancePath, [{componentId: "test", instanceNumber: 0}]);
			provide(injectionKeys.flattenedInstancePath, "test:0");
			provide(injectionKeys.core, wf as any);
			return { args, rootStyle };
		},
		template:
			'<div :style="rootStyle"><${component.nameTrim} /></div>',
	}),
};
	`;
}

export async function generate() {
	const components = await loadComponents();

	for (const component of components) {
		if (IGNORE_COMPONENT_TYPES.includes(component.type)) {
			continue;
		}
		component.nameTrim = component.name.replaceAll(/\s/g, "");
		const name = path.basename(component.fileRef, ".vue");
		const mod = component.fileRef.split("/").slice(-2, -1)[0];
		// eslint-disable-next-line no-console
		console.log("Generating ", storyFile(mod, name + ".stories.ts"));
		if (!existsSync(storyFile(mod))) {
			await fs.mkdir(storyFile(mod), { recursive: true });
		}
		const filePath = storyFile(mod, name + ".stories.ts");

		await fs.writeFile(
			filePath,
			generateImports(component, {
				filePath,
				srcPath: (...path) => relPath(filePath, srcFile(...path)),
			}) +
				generateMeta(component, { module: mod }) +
				generateStory(component),
			{
				flag: "w+",
			},
		);
	}
}
